Простые приложения на C#
Простые приложения на C#
Язык программирования C# сочетает в себе мощь объектно-ориентированного подхода, строгую типизацию и высокую производительность. Консольные приложения на этом языке служат идеальной основой для понимания фундаментальных концепций: работы с памятью, управления потоками выполнения, обработки исключений и взаимодействия с операционной системой. Ниже представлены примеры реализации типовых утилит, демонстрирующих ключевые возможности платформы .NET.
Генератор случайных паролей
Генерация надежных паролей требует комбинации различных символов из заданных наборов. В C# для этого используют класс Random или более современный Система.RandomNumberGenerator. Работа со строками осуществляется через методы преобразования массивов символов в строки.
Пример кода
using Система;
using Система.Linq;
using Система.Безопасность.Cryptography;
class PasswordGenerator
{
static void Main()
{
int length = 16;
string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";
// Использование криптографически стойкого генератора
var randomBytes = new byte[length];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomBytes);
}
char[] passwordChars = new char[length];
for (int i = 0; i < length; i++)
{
int index = randomBytes[i] % chars.Length;
passwordChars[i] = chars[index];
}
string password = new string(passwordChars);
Console.WriteLine($"Сгенерированный пароль: {password}");
}
}
Разбор логики
Код создает массив байтов фиксированного размера. Метод GetBytes заполняет этот массив криптографически стойкими случайными значениями. Цикл перебирает каждый байт, вычисляет остаток от деления на длину набора допустимых символов и выбирает соответствующий символ. Результат собирается в строку и выводится в консоль. Использование RandomNumberGenerator предпочтительнее обычного Random, так как он обеспечивает непредсказуемость значений, необходимую для безопасности.
Сортировщик текстового файла
Работа с файловой системой включает чтение содержимого, обработку данных в памяти и запись результата обратно. Классы File и StreamReader/StreamWriter предоставляют удобный интерфейс для этих операций. Список строк сортируется методом Sort, который использует алгоритм быстрой сортировки.
Пример кода
using Система;
using Система.Collections.Generic;
using Система.IO;
using Система.Linq;
class TextSorter
{
static void Main(string[] args)
{
if (args.Length < 2)
{
Console.WriteLine("Использование: SortText.exe <input_file> <output_file>");
return;
}
string inputFile = args[0];
string outputFile = args[1];
if (!File.Exists(inputFile))
{
Console.WriteLine("Ошибка: Входной файл не найден.");
return;
}
// Чтение всех строк из файла
List<string> lines = File.ReadAllLines(inputFile).ToList();
// Сортировка строк по алфавиту (независимо от регистра)
lines.Sort((a, b) => string.Compare(a, b, StringComparison.OrdinalIgnoreCase));
// Запись отсортированных строк
File.WriteAllLines(outputFile, lines);
Console.WriteLine($"Файл '{outputFile}' успешно создан с {lines.Count} строками.");
}
}
Разбор логики
Метод ReadAllLines загружает все строки файла в список. Лямбда-выражение внутри метода Sort определяет правило сравнения: строки сравниваются без учета регистра. После сортировки метод WriteAllLines записывает обновленный список в новый файл. Обработка ошибок проверяет существование входного файла перед началом работы.
Консольный калькулятор
Арифметические операции реализуются через условные конструкции. Парсинг пользовательского ввода требует разделения строки на компоненты. Обработка исключений защищает программу от некорректных данных.
Пример кода
using Система;
class Calculator
{
static void Main()
{
Console.Write("Введите первое число: ");
if (!double.TryParse(Console.ReadLine(), out double num1))
{
Console.WriteLine("Ошибка: Неверный формат первого числа.");
return;
}
Console.Write("Выберите операцию (+, -, *, /): ");
string operation = Console.ReadLine();
Console.Write("Введите второе число: ");
if (!double.TryParse(Console.ReadLine(), out double num2))
{
Console.WriteLine("Ошибка: Неверный формат второго числа.");
return;
}
double result = 0;
bool validOperation = true;
switch (operation)
{
case "+":
result = num1 + num2;
break;
case "-":
result = num1 - num2;
break;
case "*":
result = num1 * num2;
break;
case "/":
if (num2 == 0)
{
Console.WriteLine("Ошибка: Деление на ноль невозможно.");
validOperation = false;
}
else
{
result = num1 / num2;
}
break;
default:
Console.WriteLine("Ошибка: Неизвестная операция.");
validOperation = false;
break;
}
if (validOperation)
{
Console.WriteLine($"Результат: {result}");
}
}
}
Разбор логики
Программа последовательно запрашивает данные у пользователя. Метод TryParse пытается преобразовать введенную строку в число типа double. Если преобразование не удается, программа сообщает об ошибке и завершается. Операция выбирается через конструкцию switch. При делении выполняется проверка делителя на ноль. Результат выводится только если операция выполнена корректно.
Трекер задач в формате JSON
Сертификация и десериализация объектов позволяют хранить сложные структуры данных в текстовом виде. Библиотека Система.Text.Json является стандартным инструментом для работы с JSON в современных версиях .NET.
Пример кода
using Система;
using Система.Collections.Generic;
using Система.IO;
using Система.Text.Json;
class TaskTracker
{
class TaskItem
{
public int Id { get; set; }
public string Description { get; set; }
public bool IsCompleted { get; set; }
}
static void Main()
{
string filePath = "Задачи.json";
List<TaskItem> Задачи = LoadTasks(filePath);
Console.WriteLine("Добавьте новую задачу (введите описание, 'q' для выхода):");
while (true)
{
string input = Console.ReadLine();
if (input.ToLower() == "q") break;
int id = Задачи.Count > 0 ? Задачи.Max(t => t.Id) + 1 : 1;
Задачи.Add(new TaskItem { Id = id, Description = input, IsCompleted = false });
SaveTasks(Задачи, filePath);
Console.WriteLine($"Задача #{id} добавлена.");
}
Console.WriteLine("\nВсе задачи:");
foreach (var task in Задачи)
{
string status = task.IsCompleted ? "[x]" : "[ ]";
Console.WriteLine($"{status} {task.Id}: {task.Description}");
}
}
static List<TaskItem> LoadTasks(string path)
{
if (!File.Exists(path)) return new List<TaskItem>();
string json = File.ReadAllText(path);
return JsonSerializer.Deserialize<List<TaskItem>>(json) ?? new List<TaskItem>();
}
static void SaveTasks(List<TaskItem> Задачи, string path)
{
string json = JsonSerializer.Serialize(Задачи);
File.WriteAllText(path, json);
}
}
Разбор логики
Класс TaskItem описывает структуру одной записи. Метод LoadTasks читает файл и преобразует JSON-строку в список объектов. Если файл отсутствует, возвращается пустой список. Метод SaveTasks выполняет обратное действие: объект сериализуется в JSON и записывается в файл. Цикл while позволяет пользователю добавлять задачи до тех пор, пока не будет введена команда выхода.
Простой HTTP-сервер и клиент
Класс HttpListener позволяет создавать сервера, принимающие запросы. Класс HttpClient используется для отправки запросов к этим серверам. Это базовый пример сетевой коммуникации без использования сторонних фреймворков.
Пример кода Сервера
using Система;
using System.Net;
using Система.Threading.Задачи;
class SimpleServer
{
static async Task Main()
{
string prefix = "http://localhost:8080/";
HttpListener listener = new HttpListener();
listener.Prefixes.Add(prefix);
listener.Start();
Console.WriteLine("Сервер запущен. Нажмите Ctrl+C для остановки.");
try
{
while (true)
{
HttpListenerContext context = await listener.GetContextAsync();
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
string responseBody = $"Получен запрос: {request.Url.PathAndQuery}";
byte[] buffer = Система.Text.Encoding.UTF8.GetBytes(responseBody);
response.ContentLength64 = buffer.Length;
Система.IO.Stream output = response.OutputStream;
await output.WriteAsync(buffer, 0, buffer.Length);
output.Close();
}
}
finally
{
listener.Stop();
}
}
}
Пример кода Клиента
using Система;
using System.Net.Http;
using Система.Threading.Задачи;
class SimpleClient
{
static async Task Main()
{
using HttpClient client = new HttpClient();
string url = "http://localhost:8080/test";
try
{
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Ответ сервера: {body}");
}
catch (HttpRequestException e)
{
Console.WriteLine($"Ошибка запроса: {e.Message}");
}
}
}
Разбор логики
Сервер создает экземпляр HttpListener, регистрирует адрес и запускает цикл ожидания запросов. Получив контекст, сервер формирует ответ, конвертирует текст в байты и записывает его в поток вывода. Клиент создает HttpClient, отправляет GET-запрос и ожидает ответ. Метод EnsureSuccessStatusCode выбрасывает исключение при ошибках HTTP (например, 404 или 500). Асинхронные методы await предотвращают блокировку потока во время сетевого ожидания.
Утилита для сканирования директорий
Рекурсивный обход файловых структур реализуется через метод Directory.GetFiles с указанием параметра поиска поддиректорий. Информация о каждом файле доступна через свойства класса FileInfo.
Пример кода
using Система;
using Система.IO;
class DirectoryScanner
{
static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Укажите путь к директории.");
return;
}
string rootPath = args[0];
if (!Directory.Exists(rootPath))
{
Console.WriteLine("Директория не найдена.");
return;
}
string[] files = Directory.GetFiles(rootPath, "*", SearchOption.AllDirectories);
Console.WriteLine($"Найдено файлов: {files.Length}");
long totalSize = 0;
foreach (string file in files)
{
FileInfo info = new FileInfo(file);
totalSize += info.Length;
// Ограничим вывод первых 10 файлов для краткости
if (Array.IndexOf(files, file) < 10)
{
Console.WriteLine($"{info.Name} ({info.Length} байт)");
}
}
Console.WriteLine($"Общий размер: {totalSize / 1024.0:F2} КБ");
}
}
Разбор логики
Метод GetFiles принимает корневой путь, маску поиска (*) и опцию AllDirectories, которая включает подпапки в поиск. Цикл проходит по каждому файлу, создает объект FileInfo для получения метаданных (размер, имя) и суммирует общий объем. Проверка индекса массива ограничивает вывод на экран для демонстрации.
Скрипт создания резервной копии
Копирование файлов требует проверки существования источника и целевой папки. Метод File.Copy позволяет дублировать файлы, а Directory.CreateDirectory создает отсутствующие каталоги.
Пример кода
using Система;
using Система.IO;
using Система.Diagnostics;
class BackupUtility
{
static void Main(string[] args)
{
if (args.Length < 2)
{
Console.WriteLine("Использование: Backup.exe <source_folder> <destination_folder>");
return;
}
string source = args[0];
string destination = args[1];
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string backupFolder = Path.Combine(destination, $"Backup_{timestamp}");
if (!Directory.Exists(source))
{
Console.WriteLine("Источник не существует.");
return;
}
Directory.CreateDirectory(backupFolder);
int copiedCount = 0;
string[] files = Directory.GetFiles(source, "*.*", SearchOption.AllDirectories);
foreach (string file in files)
{
string relativePath = file.Substring(source.Length).TrimStart(Path.DirectorySeparatorChar);
string destFile = Path.Combine(backupFolder, relativePath);
string destDir = Path.GetDirectoryName(destFile);
Directory.CreateDirectory(destDir);
File.Copy(file, destFile, true); // true означает перезапись существующих файлов
copiedCount++;
}
Console.WriteLine($"Резервная копия создана в: {backupFolder}");
Console.WriteLine($"Скопировано файлов: {copiedCount}");
}
}
Разбор логики
Программа получает имена исходной и целевой папок. Текущая дата и время используются для формирования уникального имени папки резервной копии. Создается структура папок, повторяющая исходную. Цикл копирует каждый файл, сохраняя относительный путь. Флаг true в методе Copy разрешает перезапись файлов с таким же именем, если они уже существуют.
Мониторинг дискового пространства
Класс DriveInfo предоставляет информацию о физических и логических дисках системы. Вычисления процентов свободного места выполняются путем деления доступного объема на общий.
Пример кода
using Система;
using Система.IO;
class DiskMonitor
{
static void Main()
{
DriveInfo[] drives = DriveInfo.GetDrives();
Console.WriteLine("Статус дисков:\n");
Console.WriteLine("Диск | Объем (GB) | Свободно (GB) | Занято (%)");
Console.WriteLine("-----------------------------------------------");
foreach (DriveInfo drive in drives)
{
if (drive.DriveType == DriveType.Fixed || drive.DriveType == DriveType.Сеть)
{
double totalGB = drive.TotalSize / (1024.0 * 1024.0 * 1024.0);
double freeGB = drive.AvailableFreeSpace / (1024.0 * 1024.0 * 1024.0);
double usedPercent = ((totalGB - freeGB) / totalGB) * 100;
string label = drive.Name.TrimEnd(':');
Console.WriteLine($"{label,-4} | {totalGB,10:F2} | {freeGB,10:F2} | {usedPercent,10:F1}%");
}
}
}
}
Разбор логики
Метод GetDrives возвращает массив всех подключенных томов. Цикл фильтрует диски по типу (фиксированные и сетевые), игнорируя приводы CD/DVD. Объемы переводятся из байт в гигабайты. Процент занятого места рассчитывается как разница между общим и свободным объемом, деленная на общий объем. Вывод форматируется с использованием спецификаторов ширины поля.
Парсер URL и проверка доступности ресурса
Разбор ссылок осуществляется через класс Uri. Проверка доступности требует создания HTTP-запроса и анализа кода состояния ответа.
Пример кода
using Система;
using System.Net.Http;
class UrlChecker
{
static async Task Main()
{
string[] urls = {
"https://example.com",
"https://nonexistent-site-12345.com",
"http://localhost:8080"
};
using HttpClient client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(5);
foreach (string url in urls)
{
try
{
Uri uri = new Uri(url);
Console.WriteLine($"\nURL: {url}");
Console.WriteLine($"Хост: {uri.Host}");
Console.WriteLine($"Порт: {uri.Port}");
Console.WriteLine($"Схема: {uri.Scheme}");
HttpResponseMessage response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
Console.WriteLine($"Статус: Доступен ({(int)response.StatusCode})");
}
else
{
Console.WriteLine($"Статус: Недоступен ({(int)response.StatusCode})");
}
}
catch (Exception ex)
{
Console.WriteLine($"Ошибка: {ex.Message}");
}
}
}
}
Разбор логики
Массив ссылок содержит тестовые адреса. Цикл создает объект Uri для каждого элемента, извлекая хост, порт и схему. Настройка таймаута предотвращает зависание программы при отсутствии ответа от удаленного сервера. Метод GetAsync отправляет запрос, а свойство IsSuccessStatusCode указывает на успешность операции (код 2xx). Исключения перехватываются для обработки сетевых ошибок.
Конвертер форматов дат
Класс DateTime поддерживает различные форматы представления времени. Метод ToString преобразует дату в строку по заданному шаблону, а ParseExact выполняет обратное действие, проверяя соответствие строки шаблону.
Пример кода
using Система;
class DateConverter
{
static void Main()
{
string inputDate = "2025-11-01T14:30:00";
string formatIn = "yyyy-MM-ddTHH:mm:ss";
string formatOut = "dd.MM.yyyy HH:mm";
try
{
DateTime dt = DateTime.ParseExact(inputDate, formatIn, null);
string output = dt.ToString(formatOut);
Console.WriteLine($"Входная строка: {inputDate}");
Console.WriteLine($"Выходная строка: {output}");
Console.WriteLine($"Тип даты: {dt.Kind}");
}
catch (FormatException)
{
Console.WriteLine("Ошибка: Неправильный формат входной строки.");
}
}
}
Разбор логики
Входная строка соответствует ISO 8601. Метод ParseExact строго проверяет соответствие шаблона yyyy-MM-ddTHH:mm:ss. Если формат не совпадает, выбрасывается исключение. Результат преобразуется в формат dd.MM.yyyy HH:mm с помощью метода ToString. Свойство Kind показывает локальное или универсальное время.
Утилита для просмотра запущенных процессов
Класс Process из пространства имен Система.Diagnostics позволяет взаимодействовать с процессами операционной системы. Можно получить список всех активных процессов, их идентификаторы и названия.
Пример кода
using Система;
using Система.Diagnostics;
class ProcessViewer
{
static void Main()
{
Process[] processes = Process.GetProcesses();
Console.WriteLine($"Активных процессов: {processes.Length}\n");
Console.WriteLine("PID | Имя процесса | Время CPU (сек)");
Console.WriteLine("--------------------------------------------------");
// Ограничиваем вывод первыми 20 процессами для читаемости
int count = 0;
foreach (Process proc in processes)
{
if (count >= 20) break;
try
{
string name = proc.ProcessName;
long cpuSeconds = (long)(proc.TotalProcessorTime.TotalSeconds);
int pid = proc.Id;
Console.WriteLine($"{pid,-8} | {name,-18} | {cpuSeconds}");
}
catch (InvalidOperationException)
{
// Процесс завершился до чтения данных
continue;
}
catch (Система.ComponentModel.Win32Exception)
{
// Нет прав доступа к процессу
continue;
}
count++;
}
}
}
Разбор логики
Метод GetProcesses возвращает массив всех процессов, запущенных текущим пользователем. Цикл проходит по списку, пытаясь прочитать свойства ProcessName, Id и TotalProcessorTime. Обработка исключений необходима, так как процессы могут завершиться в момент чтения или требовать повышенных привилегий. Ограничение количества выводимых строк делает вывод удобным для восприятия в консоли.
Характерный пример для языка C#
Особенностью C# является использование событий и делегатов для реализации реактивного поведения. Приведенный пример демонстрирует создание простого механизма подписки на события, что характерно для архитектуры приложений на базе .NET.
Пример кода
using Система;
// Определение делегата
public delegate void MessageHandler(string message);
// Класс, генерирующий события
public class EventPublisher
{
public event MessageHandler OnMessageReceived;
public void Broadcast(string msg)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Отправлено сообщение: {msg}");
OnMessageReceived?.Invoke(msg);
}
}
// Класс, подписывающийся на события
public class Subscriber
{
public void HandleMessage(string msg)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Получено: {msg}");
}
}
class Program
{
static void Main()
{
EventPublisher publisher = new EventPublisher();
Subscriber sub1 = new Subscriber();
Subscriber sub2 = new Subscriber();
// Подписка на событие
publisher.OnMessageReceived += sub1.HandleMessage;
publisher.OnMessageReceived += sub2.HandleMessage;
// Генерация события
publisher.Broadcast("Привет, мир!");
publisher.Broadcast("C# мощный язык!");
// Отписка от события
publisher.OnMessageReceived -= sub1.HandleMessage;
publisher.Broadcast("Только второй подписчик услышит это.");
}
}
Разбор логики
Делегат MessageHandler определяет сигнатуру метода, который может обрабатывать событие. Событие OnMessageReceived объявляется в классе EventPublisher. Метод Broadcast вызывает обработчики, используя оператор ?.Invoke, что безопасно даже при отсутствии подписчиков. Классы Subscriber реализуют метод обработки. В методе Main происходит регистрация обработчиков через оператор += и удаление через -=. Это паттерн наблюдателя, широко используемый в UI-фреймворках и системах сообщений.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Исходный код хранится в файлах .cs для C. Там пишется логика приложения. В проекте можно создавать новые файлы, и через внутреннее API платформы будет взаимодействие между ними. Допустим, можно… Фундамент для начинающего программиста - что повторить, как работать, чего ожидать. Справочник-шпаргалка по конфигурациям в C — типы, синтаксис, стандартная библиотека, типовые паттерны. Не заменяет пошаговое обучение. Учебный курс — раздел. Набор советов, правил, принципов и обычаев в разработке на этом языке. Кавычки, точки, запятые, скобки и прочие знаки препинания. Ключевое слово Назначение Пример ------------------------------------ if Условное выполнение блока кода при истинности выражения if (count 0) Process(); else Альтернативное выполнение при ложности… Набор функций, которые включены в стандартную библиотеку языка. И если глобальные пространства имён применяются для общего и не используются для всего подряд, то в каждом файле добавляются свои, нужные для кода файла пространства - это file-scoped namespaces,… манипулировать данными (арифметические, логические, сравнительные операторы). Самый базовый способ ветвления — оператор if. Он проверяет условие и, если оно истинно (true), выполняет блок кода. Ошибка (error) — это, как правило, системный сбой, который невозможно обработать (например, нехватка памяти). Исключение (exception) — это управляемое отклонение, которое можно предвидеть,… Платформо-зависимые исключения — например, PlatformNotSupportedException используется в кроссплатформенных API, когда функция недоступна на текущей ОС.C# - язык программирования платформы .NET
Что требуется знать перед началом изучения языка программирования C#
Справочник по конфигурациям в C#
Рекомендации по разработке на C#
Синтаксис и пунктуация в C#
Ключевые слова языка C#
Встроенные функции и методы C#
Пространства имён в C#
Управляющие конструкции и логические операторы
Условные выражения и ветвления
Обработка исключений в C#
Иерархия классов исключений в C#